﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using Fine.Collections.Intf;
using JetBrains.Annotations;



namespace Fine.Collections.Views
{
    /// <summary>
    /// Adapts RCollection of <b>S</b> to RCollection of <b>T</b>.
    /// </summary>
    /// <typeparam name="C">type of source collection.</typeparam>
    /// <typeparam name="S">type of item of the source collection.</typeparam>
    /// <typeparam name="T">type of item ot the result collection.</typeparam>
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(RCollectionAdaptor<,,>.CollectionDebugView))]
    public class RCollectionAdaptor<C,S,T> : RCollection<T>
        where C: RCollection<S>
    {
        [NotNull]
        protected readonly C source;

        [NotNull]
        protected readonly Converter<S,T> converter;


        /// <summary>
        /// Creates an adaptor.
        /// </summary>
        /// <param name="source">source collection.</param>
        /// <param name="converter">items convertor.</param>
        public RCollectionAdaptor([NotNull] C source, [NotNull] Converter<S, T> converter)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (converter == null) throw new ArgumentNullException("converter");
            this.source = source;
            this.converter = converter;
        }


        public int Count
        {
            get { return source.Count; }
        }

        public bool IsEmpty
        {
            get { return source.IsEmpty; }
        }

        public bool IsNotEmpty
        {
            get { return source.IsNotEmpty; }
        }


        [NotNull]
        public SearchResult<T> Find(Predicate<T> predicate)
        {
            var result = source.Find(x => predicate(converter(x)));
            return result.Found ? SearchResults<T>.Found(converter(result.Item)) 
                                : SearchResults<T>.NotFound();
        }


        public T this[Predicate<T> predicate]
        {
            get { return Find(predicate).Item; }
        }


        public void ProcessAll(Action<T> action)
        {
            source.ProcessAll(item => action(converter(item)));
        }


        public RIterator<T> TakeIterator()
        {
            return new RIteratorAdaptor<S,T>(source.TakeIterator(), converter);
        }


        IEnumerator IEnumerable.GetEnumerator()
        {
            return new RIteratorAdaptor<S,T>(source.TakeIterator(), converter);
        }


        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return new RIteratorAdaptor<S,T>(source.TakeIterator(), converter);
        }



        #region DEBUG HELPER

        /// <summary>
        /// Debug helper.
        /// </summary>
        internal class CollectionDebugView
        {
            [NotNull]
            private readonly RCollection<T> collection;


            public CollectionDebugView([NotNull] RCollection<T> collection)
            {
                this.collection = collection;
            }


            [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
            public Array Items
            {
                get
                {
                    var debugArray = DebugViewUtils.MakeArray<T>(1, collection.Count);
                    int k = 0;
                    collection.ProcessAll(x => debugArray.SetValue(x,++k));
                    return debugArray;
                }
            }
        }

        #endregion

    }
}
